/**
 * 
 */
package programs;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import bitmap.BitMapFile;
import btree.*;
import bufmgr.PageNotReadException;
import columnar.*;
import diskmgr.PCounter;
import global.*;
import heap.*;
import index.ColumnIndexScan;
import index.IndexException;
import index.IndexScan;
import index.UnknownIndexTypeException;
import iterator.*;

/**
 * @author usr
 *
 */
public class  delete_query {
    private static short REC_LEN_STRING = 160;
    private static short REC_LEN_INT = 32;
    
	private static 	Columnarfile cf = null;
	private static 	String columnDBName = "";
	private static 	String columnarFileName = "";
	private static 	String[] targetColumnNames = null;
	private static 	String[] valueConstraint = null;
	private static  int deleteFashion = 1;
	
	public static void main(String[] args) throws Exception{

		if(args.length != 7)
		{
			System.out.println("The command line invocation must be as follows: \n\t"
                    + "delete_query COLUMNDBNAME COLUMNARFILENAME [TargetColumnNames] "
                    + "ValueConstraint NumBuf AcessType DELETEFASHION");
			Runtime.getRuntime().exit(1);
		}   
		
		columnDBName = "";
		columnarFileName = "";
		targetColumnNames = null;
		valueConstraint = null;
		String numBuf = "";
		int accessType = 0;

		columnDBName = 		args[0];
		columnarFileName = 	args[1];
		targetColumnNames = args[2].replace('[', ' ').replace(']', ' ').trim().split(",");
		valueConstraint = 	args[3].replace('[', ' ').replace(']', ' ').trim().split(",");
		numBuf = 			args[4];
		
		if(args[5].equals("FILESCAN"))
		    accessType= QueryType.FileScan;
		else if(args[5].equals("COLUMNSCAN"))
		    accessType= QueryType.ColumnScan;
		else if(args[5].equals("BTREE"))
		    accessType= QueryType.BTree;
		else if(args[5].equals("BITMAP"))
		    accessType= QueryType.BitMap;
		else
		{
		        System.out.println("Only AccessTypes allowed:FILESCAN, COLUMNSCAN, BTREE or BITMAP");
		        Runtime.getRuntime().exit(1);
		}
		
		deleteFashion =         Integer.parseInt(args[6]);

	    // read an existing DB into BM
	    global.SystemDefs sysdef = new global.SystemDefs(columnDBName,0,Integer.parseInt(numBuf),"Clock");
	    
	    //try read a columnarfile form DB
	    try{
	            cf = new Columnarfile(columnarFileName);
	    }
	    catch(Exception ex){
	            System.out.println("Unable to read a columnar file from DB\n\t check if it has been created first\n");
	            Runtime.getRuntime().exit(1);
	    }
	    
	    // execute query
	    switch(accessType)
	    {
	    	case QueryType.FileScan:
	    		DoFileScan();
	    		break;
	    	case QueryType.ColumnScan:
	    		DoColumnScan();
		    	break;
	    	case QueryType.BTree:
	    		DoBTree();
		    	break;
	    	case QueryType.BitMap:
	    		DoBitMap();
		    	break;
		    default:
	            System.out.println("Unable to identify ACCESSTYPE parameter, please check your input\n");
	            Runtime.getRuntime().exit(1);
	    }
	    
        sysdef.JavabaseBM.flushAllPages();
	    
	    //print number of processed pages 
	    PCounter.print();

	}
	
	
	static private void DoFileScan() throws Exception
	{
		AttrType[] types = new AttrType[targetColumnNames.length];
		short[] sizes = new short[targetColumnNames.length];
		
	    FldSpec [] projection = new FldSpec[targetColumnNames.length];
	    for(int i = 0;i<targetColumnNames.length;i++)
	    {
	    	projection[i] = new FldSpec(new RelSpec(RelSpec.outer), i+1);
	    	types[i] = cf.getColumnType(targetColumnNames[i]);
	    	if(types[i].attrType == AttrType.attrInteger)
	    		sizes[i] = REC_LEN_INT;
	    	else
	    		sizes[i] = REC_LEN_STRING;
	    }
		
        //expr select expression
        CondExpr[] expr = new CondExpr[2];
        expr[0] = new CondExpr();
        expr[0].op = new AttrOperator(Integer.valueOf(valueConstraint[1]));
        expr[0].type1 = new AttrType(AttrType.attrSymbol);
        expr[0].type2 = cf.getColumnType(valueConstraint[0]);
        expr[0].operand1.symbol = new FldSpec(new RelSpec(RelSpec.outer), cf.getColumnOffset(valueConstraint[0]));
        
        if(expr[0].type2.attrType == AttrType.attrInteger)
        	expr[0].operand2.integer= Integer.parseInt(valueConstraint[2]);
        else
        	expr[0].operand2.string = valueConstraint[2];

        expr[1] = null;
		
	    ColumnarFileScan cfs = null;
	    try {
	    	cfs  = new ColumnarFileScan(cf.columnarFileName(), types, sizes,(short)targetColumnNames.length, (short)targetColumnNames.length, projection, expr);
	    }
	    catch (Exception e) {
	    	System.err.println (""+e);
	    }
	    int delCounter = 0;
	    
	    
	    
	    
        Sort sort = cf.startSortDelMethod();
        Tuple t = null;
        try{
	        if((t = sort.get_next()) == null)
	        {
			    Tuple tuple = new Tuple();
				while ((tuple = cfs.get_next()) != null) {
        			cf.InsertDeletedPosition(cfs.GetCurrentPostion());
        			delCounter++;
				}
	        }
	        else
	        {
	            
	        	List<Integer> positions = new ArrayList<Integer>();
                do{
                    positions.add(t.getIntFld(1));
                }while((t = sort.get_next()) != null);
                Tuple tuple = null;
                
		        while((tuple = cfs.get_next()) != null)
                {
                    Integer val = cfs.GetCurrentPostion();
                    if(positions.contains(val))
                        break;
                    
        			cf.InsertDeletedPosition(cfs.GetCurrentPostion());
        			delCounter++;
                } 
	        }
        }catch(Exception ex){}
        finally{
	        sort.close();
			cfs.close();
        }    
	    

		
		cfs.close();
		
	    if(deleteFashion == 1 && delCounter != 0)
	    {
	        cf.purgeAllDeletedTuples();
	        System.out.println(delCounter + " records have been permanently deleted");
	    }
	    else if(deleteFashion == 0 && delCounter != 0)
	    	System.out.println(delCounter + " records have been marked as deleted");
	    else
	    	System.out.println("No records were found to be deleted");
	}
	
	static private void DoColumnScan() throws InvalidSlotNumberException, HFException, HFDiskMgrException, HFBufMgrException, Exception
	{
		AttrType[] types = {cf.getColumnType(valueConstraint[0])};
		short[] sizes = {REC_LEN_STRING};
		
	    FldSpec [] projection = new FldSpec[1];
	    projection[0] = new FldSpec(new RelSpec(RelSpec.outer), 1);
		
        //expr select expression
        CondExpr[] expr = new CondExpr[2];
        expr[0] = new CondExpr();
        expr[0].op = new AttrOperator(Integer.valueOf(valueConstraint[1]));
        expr[0].type1 = new AttrType(AttrType.attrSymbol);
        expr[0].type2 = types[0];
        expr[0].operand1.symbol = new FldSpec(new RelSpec(RelSpec.outer), 1);
        
        if(types[0].attrType == AttrType.attrInteger)
        	expr[0].operand2.integer= Integer.parseInt(valueConstraint[2]);
        else
        	expr[0].operand2.string = valueConstraint[2];

        expr[1] = null;
		
	    FileScan fs = null;
	    try {
	    	fs  = new FileScan(cf.getColumnFileName(valueConstraint[0]), types, sizes,(short)1, (short)1, projection, expr);
	    }
	    catch (Exception e) {
	    	System.err.println (""+e);
	    }
	    	
	    int delCounter = 0;
        Sort sort = cf.startSortDelMethod();
        Tuple t = null;
        try{
	        if((t = sort.get_next()) == null)
	        {
			    Tuple tuple = new Tuple();
				while ((tuple = fs.get_next()) != null) {
					cf.InsertDeletedPosition(fs.getCurrentRecordPosition());
					delCounter++;
				}
	        }
	        else
	        {
	            
	        	List<Integer> positions = new ArrayList<Integer>();
                do{
                    positions.add(t.getIntFld(1));
                }while((t = sort.get_next()) != null);
                Tuple tuple = null;
                
		        while((tuple = fs.get_next()) != null)
                {
                    Integer val = fs.getCurrentRecordPosition();
                    if(positions.contains(val))
                        break;
        			cf.InsertDeletedPosition(fs.getCurrentRecordPosition());
        			delCounter++;
                } 
	        }
        }catch(Exception ex){}
        finally{
	        sort.close();
			fs.close();
        }
		
		fs.close();
		
	    if(deleteFashion == 1 && delCounter != 0)
	    {
	        cf.purgeAllDeletedTuples();
	        System.out.println(delCounter + " records have been permanently deleted");
	    }
	    else if(deleteFashion == 0 && delCounter != 0)
	    	System.out.println(delCounter + " records have been marked as deleted");
	    else
	    	System.out.println("No records were found to be deleted");
	    	
	}
	
	
	static private void DoBTree() throws JoinsException, PageNotReadException, TupleUtilsException, PredEvalException, SortException, LowMemException, UnknowAttrType, UnknownKeyTypeException, Exception
	{
		AttrType[] types = {cf.getColumnType(valueConstraint[0])};
		short[] sizes = {REC_LEN_STRING};
		
	    FldSpec [] projection = new FldSpec[1];
	    projection[0] = new FldSpec(new RelSpec(RelSpec.outer), 1);
		
        //expr select expression
        CondExpr[] expr = new CondExpr[2];
        expr[0] = new CondExpr();
        expr[0].op = new AttrOperator(Integer.valueOf(valueConstraint[1]));
        expr[0].type1 = new AttrType(AttrType.attrSymbol);
        expr[0].type2 = types[0];
        expr[0].operand1.symbol = new FldSpec(new RelSpec(RelSpec.outer), 1);
        
        if(types[0].attrType == AttrType.attrInteger)
        	expr[0].operand2.integer= Integer.parseInt(valueConstraint[2]);
        else
        	expr[0].operand2.string = valueConstraint[2];

        expr[1] = null;
        
        String fileName = cf.getColumnFileName(valueConstraint[0]);
    	
        
	    Iterator it = new IndexScan(new IndexType(IndexType.B_Index), 
	    		fileName,
	    		fileName + ".btree", 
	    		types, sizes, 1, 1, projection, expr, 1, true);
        
	    //Iterator it = new ColumnIndexScan(new IndexType(IndexType.B_Index), 
	    //		fileName, 
	    //		fileName + ".btree",
	    //		types[0], 
	    //		 size, expr, false);

	    Tuple tuple = null;
		while ((tuple = it.get_next()) != null) {
			tuple.print(types);
		}
		
		it.close();
	}
	
	static private void DoBitMap()
	{
		BitMapFile bmf;
		RID rid;
		try {
			 bmf= new BitMapFile(cf
					.getColumnFileName(targetColumnNames[0])
					+ "." + "bitmap" + "." + valueConstraint[2]);
		} catch (Exception e) {
			System.out.println("The bitmap does not exist, please use program index to create bitmap first.");
			Runtime.getRuntime().exit(1);
		}
		//rid=bmf.getHeaderPage().
	}


}